/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.scoring;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import edu.ucsd.db.bassarisk.exception.VisitorException;
import edu.ucsd.db.bassarisk.query.Constants;
import edu.ucsd.db.bassarisk.query.path.DSDElementVisitor;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDAggregate;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDContext;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDDescent;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDElement;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDFilter;
import edu.ucsd.db.bassarisk.schema.DataSequenceClass;
import edu.ucsd.db.bassarisk.schema.RunClass;
import edu.ucsd.db.bassarisk.schema.SchemaNode;
import edu.ucsd.db.bassarisk.schema.SchemaRunNode;
import edu.ucsd.db.bassarisk.treepattern.DescentAxis;

public class ContextsPathExpansionVisitor implements
    DSDElementVisitor<DSDElement, Void> {
  private final DataSequenceClass answerContextClass;
  private final DataSequenceClass queryRootClass;
  private final Comparator<DataSequenceClass> runClassComparator;

  public ContextsPathExpansionVisitor(final Comparator<DataSequenceClass> runClassComparator,
                                      final DataSequenceClass answerContextClass2,
                                      final DataSequenceClass queryRootClass2) {
    this.runClassComparator = runClassComparator;
    this.answerContextClass = answerContextClass2;
    this.queryRootClass = queryRootClass2;
  }

  private List<RunClass> expandPath() {
    List<RunClass> superClasses = new ArrayList<RunClass>();
    for (SchemaNode runNode : this.answerContextClass.getRefinement()) {
      SchemaNode parent = runNode;
      while (parent != null) {
        if (parent instanceof SchemaRunNode) {
          RunClass superClass = new RunClass(parent.getName());
          int i = this.runClassComparator.compare(this.queryRootClass, superClass);
          if (i > 0) {
            superClasses.add(superClass);
          }
        }
        parent = parent.getParent();
      }
    }
    Collections.sort(superClasses, this.runClassComparator);
    Collections.reverse(superClasses);
    return superClasses;
  }

  @Override
  public DSDElement visitAggregate(final DSDAggregate elem, final Void args)
      throws VisitorException {
    String className = elem.getSequenceClass().getName();
    if (className.equals(Constants.ContextsPath)) {
      // In this case, the aggregate actually goes on the LAST element of the
      // path
      List<RunClass> superClasses = this.expandPath();
      DSDElement tail = elem.getNext();
      for (int i = superClasses.size() - 1; i >= 0; --i) {
        if (i > 0) {
          DSDElement newTail =
              new DSDDescent(DescentAxis.Child, superClasses.get(i));
          newTail.setNext(tail);
          tail = newTail;
        } else {
          DSDElement newTail =
              new DSDAggregate(DescentAxis.Child, superClasses.get(i),
                               elem.getFunction());
          newTail.setNext(tail);
          return newTail;
        }
      }
    } else if (elem.hasNext()) {
      elem.setNext(elem.getNext().accept(this, args));
      return elem;
    }
    return null;
  }

  @Override
  public DSDElement visitContext(final DSDContext elem, final Void args)
      throws VisitorException {
    return elem;
  }

  @Override
  public DSDElement visitDescent(final DSDDescent elem, final Void args)
      throws VisitorException {
    String className = elem.getSequenceClass().getName();
    if (className.equals(Constants.ContextsPath)) {
      List<RunClass> superClasses = this.expandPath();
      DSDElement tail = elem.getNext();
      for (int i = superClasses.size() - 1; i >= 0; --i) {
        DSDElement newTail = new DSDDescent(DescentAxis.Child, superClasses.get(i));
        newTail.setNext(tail);
        tail = newTail;
      }
      return tail;
    } else if (elem.hasNext()) {
      elem.setNext(elem.getNext().accept(this, args));
      return elem;
    }
    return null;
  }

  @Override
  public DSDElement visitFilter(final DSDFilter elem, final Void args)
      throws VisitorException {
    return elem;
  }
}
